package fr.playsoft.psas;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Stack;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

public class ImageDownloader {
	private static final String TAG = "ImageDownloader";

	private static final int MAX_IMAGEDOWNLOADER_QUEUE_SIZE = 80;
	static private int TIMEOUT_CONNECTION = 120000; // 2 minutes
	static private int TIMEOUT_SOCKET = 60000; // 1 minutes

	//the simplest in-memory cache implementation. This should be replaced with something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
	private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>();

	/**
	 * By default we don't limit number of images cached
	 */
	private int mLimitUpperBound = -1;

	private Context mContext;
	private String mImgSavedPath;

	private Animation myFadeInAnimation = null;

	private void startFaceinAnim(ImageView imageView) {
		imageView.startAnimation(myFadeInAnimation);
	}

	public void clearAllCache() {
		//photosQueue.CleanAll();
		cache = null;
		cache=new HashMap<String, Bitmap>();
	}

	//private File cacheDir;

	public ImageDownloader(Context context, int pLimit){

		mLimitUpperBound = pLimit;
		initAll(context);
	}

	public ImageDownloader(Context context){    	
		mLimitUpperBound = -1;

		initAll(context);
	}

	private void initAll(Context context) {
		// TODO Auto-generated method stub
		mContext = context;

		//Make the background thead low priority. This way it will not affect the UI performance
		photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
		myFadeInAnimation = AnimationUtils.loadAnimation(mContext, R.anim.fadein);
		
		mImgSavedPath = mContext.getCacheDir().getPath() + "/fr.playsoft.psas/images/";
	}
	
	public String getCount() {
		if(cache == null)
			return "null";
		else
			return cache.size() + " items";
	}

	public void displayImage(String url, String filenamePath, ImageView imageView, int pLimit) {
		displayImage(url, filenamePath, imageView);
	}
	
	public void displayImage(String url, ImageView imageView) {
		displayImage(url, mImgSavedPath + getMD5Hash(url), imageView);
	}
	
	public void displayImage(String url, String filenamePath, ImageView imageView)
	{
		if(cache.containsKey(url)) {
			Bitmap bm = cache.get(url);
			if(bm != null) {
				imageView.setImageBitmap(bm);
				return;
			}
		}

		if(filenamePath != null)
		{
			File file = new File(filenamePath);
			if(file.exists())
			{
				Bitmap bmp = null;
				try{
					bmp = BitmapFactory.decodeFile(filenamePath);
				}
				catch (OutOfMemoryError e) {
					System.gc();
					// TODO: handle exception
				}
				if(bmp != null) {
					imageView.setImageBitmap(bmp);
					startFaceinAnim(imageView);

					if(mLimitUpperBound != -1) {
						int curHashmapSize = cache.size();
						if(curHashmapSize > mLimitUpperBound){
							String firstItemKey = (String) cache.keySet().toArray()[0];
							cache.remove(firstItemKey);
						}
					}

					cache.put(url, bmp);
				}
			}
			else
			{
				if(photosQueue.photosToLoad.size() < MAX_IMAGEDOWNLOADER_QUEUE_SIZE)
					queuePhoto(url, filenamePath, imageView);
			}    
		}
		else
		{
			if(cache.containsKey(url)) {
				//    			CLog.i(LOG_TAG, "displayImage cache.containsKey() fname=" + filenamePath);
				imageView.setImageBitmap(cache.get(url));
			}
			else
			{
				if(photosQueue.photosToLoad.size() < MAX_IMAGEDOWNLOADER_QUEUE_SIZE)
					queuePhoto(url, filenamePath, imageView);
			}    
		}
	}

	private void queuePhoto(String url, String filenamePath, ImageView imageView)
	{
		//This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them. 
		photosQueue.Clean(imageView);
		{
			PhotoToLoad p=new PhotoToLoad(url, filenamePath, imageView);
			synchronized(photosQueue.photosToLoad){
				photosQueue.photosToLoad.push(p);
				photosQueue.photosToLoad.notifyAll();
			}

			//start thread if it's not started yet
			if(photoLoaderThread.getState()==Thread.State.NEW)
				photoLoaderThread.start();
		}
	}

	private Bitmap getBitmap(String url, String filenamePath) 
	{ 
		Bitmap bitmap = LoadImage(url);   	

		// Save file
		if(bitmap != null)
			Save_to_Cache (bitmap, filenamePath);
		//

		return bitmap; 
	}

	static public void Save_to_Cache (Bitmap bm, String filenamePath)
	{	
		OutputStream outStream = null;
		try{
			File file = new File(filenamePath);
			try {
				boolean isGifImage = (filenamePath.indexOf(".gif") > 0);
				outStream = new FileOutputStream(file);
				CompressFormat format = (!isGifImage)? Bitmap.CompressFormat.JPEG : Bitmap.CompressFormat.PNG;
				bm.compress(format, 70, outStream);
				try{
					outStream.flush();
					try{
						outStream.close();
					}
					catch (IOException e) {
						// TODO: handle exception
					}
				}
				catch (IOException e) {
					// TODO: handle exception
				}

			} catch (FileNotFoundException e) {

				//e.printStackTrace();	
			} 
		}
		catch (NullPointerException e) {
			// TODO: handle exception
		}		
	}

	//Task for the queue
	private class PhotoToLoad
	{
		public String url;
		public ImageView imageView;
		public String mFilenamePath;
		public PhotoToLoad(String u, String filenamePath, ImageView i){
			url=u; 
			imageView=i;
			mFilenamePath = filenamePath;
		}
	}

	PhotosQueue photosQueue=new PhotosQueue();

	public void stopThread()
	{
		photoLoaderThread.interrupt();
	}

	//stores list of photos to download
	class PhotosQueue
	{
		private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

		//removes all instances of this ImageView
		public void Clean(ImageView image)
		{
			for(int j=0 ;j<photosToLoad.size();){
				if(photosToLoad.get(j).imageView==image)
					photosToLoad.remove(j);
				else
					++j;
			}
		}
		//removes all instances of this ImageView
		public void CleanAll()
		{
			for(int j=0 ;j<photosToLoad.size();){
				photosToLoad.remove(j);

			}
		}
	}

	class PhotosLoader extends Thread {
		public void run() {
			try {
				while(true)
				{
					//thread waits until there are any images to load in the queue
					if(photosQueue.photosToLoad.size()==0)
						synchronized(photosQueue.photosToLoad){
							photosQueue.photosToLoad.wait();
						}
					if(photosQueue.photosToLoad.size()!=0)
					{
						PhotoToLoad photoToLoad;
						synchronized(photosQueue.photosToLoad){
							photoToLoad=photosQueue.photosToLoad.pop();
						}
						Bitmap bmp=getBitmap(photoToLoad.url, photoToLoad.mFilenamePath);

						if(bmp != null)
						{
							if(photoToLoad.mFilenamePath == null) {
								if(mLimitUpperBound != -1) {
									int curHashmapSize = cache.size();
									if(curHashmapSize > mLimitUpperBound){
										String firstItemKey = (String) cache.keySet().toArray()[0];
										cache.remove(firstItemKey);
									}
								}

								cache.put(photoToLoad.url, bmp);
							}
							Object tag=photoToLoad.imageView.getTag();

							//if(tag!=null && ((String)tag).equals(photoToLoad.url)){
								//if(tag!=null )
								{
									BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
									try{
										Activity a=(Activity)photoToLoad.imageView.getContext();
										a.runOnUiThread(bd);
									}catch(ClassCastException cex){
										cex.printStackTrace();
									}
								}
						}
					}
					if(Thread.interrupted())
						break;
				}
			} catch (InterruptedException e) {
				//allow thread to exit
			}
		}
	}

	PhotosLoader photoLoaderThread=new PhotosLoader();

	//Used to display bitmap in the UI thread
	class BitmapDisplayer implements Runnable
	{
		Bitmap bitmap;
		ImageView imageView;
		public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
		public void run()
		{
			if(bitmap!=null) {
				imageView.setImageBitmap(bitmap);
				//            	startFaceinAnim(imageView);
			}
			else {
				Log.e("ImgD", "BitmapDisplayer setBitmap-dumpImage");
				//imageView.setImageResource(stub_id);
			}
		}
	}

	/*    public void clearCache() {
        //clear memory cache
        cache.clear();

        //clear SD cache
        File[] files=cacheDir.listFiles();
        for(File f:files)
            f.delete();
    }*/

	public Bitmap LoadImage(String URL) {
		Bitmap bitmap = null;
		InputStream in = null;

		in = connect(URL);
		if(in != null)
		{
			try {
				bitmap = BitmapFactory.decodeStream(in);	
			} catch (OutOfMemoryError e) {
				// TODO: handle exception
				System.gc();
			}

			try{
				in.close();
			}
			catch (IOException e) {
				// TODO: handle exception
			}
		}

		return bitmap;
	}

	public static InputStream connect(String url)
	{
		//CLog.i("RestClient", "Connect @url=" + url);
		HttpGet httpGet = new HttpGet(url);

		//httpGet.setHeader("Cache-Control", "no-cache"); 

		HttpParams httpParameters = new BasicHttpParams();
		// Set the timeout in milliseconds until a connection is established.
		int timeoutConnection = TIMEOUT_CONNECTION;
		HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
		// Set the default socket timeout (SO_TIMEOUT) 
		// in milliseconds which is the timeout for waiting for data.
		int timeoutSocket = TIMEOUT_SOCKET;
		HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

		DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters);
		try {
			HttpResponse response = httpClient.execute(httpGet);
			if (response != null) {
				HttpEntity responseEntity = response.getEntity();
				InputStream instream = responseEntity.getContent();
				return instream;
			}
		} catch (ClientProtocolException e) {
			//e.printStackTrace();
		} catch (IOException e) {
			//e.printStackTrace();
		}
		return null;
	}

	public static String getMD5Hash(String strMessage) {
		byte[] byteMessage;
		try {
			String result = "";
			byteMessage = strMessage.getBytes("UTF-8");

			MessageDigest md = MessageDigest.getInstance("MD5");

			byte[] strDigest = md.digest(byteMessage);

			String tmp = "";
			for (int i = 0; i < strDigest.length; i++) {
				tmp = (Integer.toHexString(0xFF & strDigest[i]));
				if (tmp.length() == 1) {
					result += "0" + tmp;
				} else {
					result += tmp;
				}
			}

			return result;

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "";
	}
}
